package com.wawu.common.httpHandler;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.wawu.common.IOC.ApplicationContext;
import com.wawu.common.annotation.controller.globalExceptionHandle.ExceptionHandler;
import com.wawu.common.annotation.controller.mapping.*;
import com.wawu.common.annotation.controller.parameter.RequestBody;
import com.wawu.common.annotation.controller.parameter.RequestParam;
import com.wawu.common.enumeration.RequestMethodEnum;
import com.wawu.common.exception.BaseException;
import com.wawu.common.property.TomcatProperty;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.ConvertUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 使用切面编程处理原生的Servlet
 * 功能：
 * 1. 路由注册以及控制层实例的映射
 * 2. 分配实际的请求相应，并以json格式放回
 * <p>
 * 包含注解（控制层相关注解的简化）：
 * 1. @RestController,@RequestMapping,@请求方式Mapping
 * 2. 请求方法接受方法相关注解：@RequestBody，@RequestParam
 */
public class AopRequestHandler {

    private static final Map<String, Method> routes = new HashMap<>();//请求的路由映射
    private static final Map<String, Object> restControllers = new HashMap<>(); // 路由对应控制器实例(来自IOC容器中的bean对象)

    //TODO：待优化： 1.如果需要简化代码：①请求路径以及方法可以放在@RequestMapping，但是这里还需要完成注解（类似`@AliasFor(annotation = Component.class)`用来处理别名）；②简单的办法可以定义一个集合来枚举
    //             2.支持多路径、多请求方式，这里默认只有一个路径
    //             3.当@请求方式Mapping为空时，特判

    /**
     * 初始化路由
     * - 注册路由
     * - 注册请求控制对应的实例
     *
     * @param context:IOC容器
     */
    public static void initializeRoutes(ApplicationContext context) {
        //获取所有被@RestController修饰的类及其方法
        for (Class<?> restcontrollerClass : context.getRestControllerClazzes()) {
            Object restController = context.getBean(restcontrollerClass);
            String basePath = "";
            //类上的是父路径
            if (restcontrollerClass.isAnnotationPresent(RequestMapping.class)) {
                basePath = restcontrollerClass.getAnnotation(RequestMapping.class).value()[0];
            }
            //类方法上还有子路径
            //合成完成的路由同时进行注册
            for (Method method : restcontrollerClass.getDeclaredMethods()) {
                String methodPath = ""; // 方法的路径，默认空
                if (method.isAnnotationPresent(GetMapping.class)) {
                    methodPath = method.getAnnotation(GetMapping.class).value()[0];
                    registerRoute(basePath, methodPath, RequestMethodEnum.GET, method, restController);
                } else if (method.isAnnotationPresent(PostMapping.class)) {
                    methodPath = method.getAnnotation(PostMapping.class).value()[0];
                    registerRoute(basePath, methodPath, RequestMethodEnum.POST, method, restController);
                } else if (method.isAnnotationPresent(PutMapping.class)) {
                    methodPath = method.getAnnotation(PutMapping.class).value()[0];
                    registerRoute(basePath, methodPath, RequestMethodEnum.PUT, method, restController);
                } else if (method.isAnnotationPresent(DeleteMapping.class)) {
                    methodPath = method.getAnnotation(DeleteMapping.class).value()[0];
                    registerRoute(basePath, methodPath, RequestMethodEnum.DELETE, method, restController);
                }
            }
        }
        System.out.println("基础请求路由：" + routes);
        System.out.println("请求控制层：" + restControllers);
        System.out.println("注意：默认是去掉Tomcat对应的上下文" + TomcatProperty.CONTEXT_PATH);
    }

    /**
     * 注册请求路径和方法映射
     *
     * @param basePath：类上的父路径
     * @param methodPath：子类上的子路径
     * @param methodEnum：请求方式
     * @param method：放射中的方法
     * @param controller：后续将要进行调用的实体类
     */
    private static void registerRoute(String basePath, String methodPath, RequestMethodEnum methodEnum, Method method, Object controller) {
        String fullPath = (basePath + methodPath).replaceAll("/+", "/");
        routes.put(methodEnum.name() + fullPath, method);
        restControllers.put(methodEnum.name() + fullPath, controller); // 记录控制器实例
    }


    /////从这里开始写注册异常处理部分/////
    //大部分其实就是参照上面
    private static final Map<Class<? extends Throwable>, Method> exceptionHandleMethod = new HashMap<>();//不同异常对应的请求方法
    private static final Map<Class<? extends Throwable>, Object> restControllerAdvices = new HashMap<>(); //全局异常处理类(来自IOC容器中的bean对象)
    //注意了下面也不管循环注解

    /**
     * 初始化全局异常处理方法
     * - 注册异常处理方法:如果为空，则获取方法参数类型(TODO：注意：这里规定异常处理方法参数)
     * - 注册异常处理对应的类
     *
     * @param context:IOC容器
     */
    public static void initializeExceptionHandlers(ApplicationContext context) {
        for (Class<?> RestControllerAdviceClass : context.getRestControllerAdviceClazzes()) {
            Object restControllerAdvice = context.getBean(RestControllerAdviceClass);

            // 遍历方法，查找@ExceptionHandler标注的方法
            for (Method method : RestControllerAdviceClass.getDeclaredMethods()) {
                if (method.isAnnotationPresent(ExceptionHandler.class)) {
                    ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class);

                    // 获取注解中指定的异常类型，如果为空，则获取方法参数类型
                    Class<? extends Throwable>[] exceptionTypes = exceptionHandler.value();
                    if (exceptionTypes.length == 0) {
                        // 如果没有指定异常类型，则取方法的第一个参数类型作为异常类型
                        Parameter[] parameters = method.getParameters();
                        if (parameters.length > 0) {
                            Class<?> parameterType = parameters[0].getType();
                            if (Throwable.class.isAssignableFrom(parameterType)) {
                                exceptionTypes = new Class[]{(Class<? extends Throwable>) parameterType};
                            }
                        }
                    }

                    // 注册异常类型和方法的映射
                    for (Class<? extends Throwable> exceptionType : exceptionTypes) {
                        exceptionHandleMethod.put(exceptionType, method);
                        restControllerAdvices.put(exceptionType, restControllerAdvice); // 记录异常处理类实例
                    }
                }
            }
        }
        System.out.println("注册的异常处理方法：" + exceptionHandleMethod);
        System.out.println("注册的异常处理控制器：" + restControllerAdvices);
    }
    /////注册异常处理部分结束/////

    /**
     * 处理请求(Servlet那边的)
     *
     * @param request
     * @param response
     * @throws IOException
     */
    public static void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String token = request.getHeader("token");
        System.out.println("token = " + token);
//        if (token == null) {
//            throw new BaseException("认证失败，请登录");
//        }
        String httpMethod = request.getMethod();//请求方式
        String relativePath = request.getRequestURI().substring(TomcatProperty.CONTEXT_PATH.length());//处理成类对应的请求路径
        //这里是存在上下文环境的，就是tomcat中会进行单独配置，所以最好就是把这个放在配置文件中
        //获取对应的处理类的路径
        // 处理上下文路径为空的情况
        //这时候访问：http://localhost:8083/api，上下文环境变为了"/api"
        //如果在tomcat中配置了上下文是正常的
        String routeKey = httpMethod + relativePath;

        Method handlerMethod = routes.get(routeKey);
        Object restController = restControllers.get(routeKey);

        System.out.println("Requested Method and Path: " + httpMethod + " " + relativePath);
        System.out.println("处理方法：" + handlerMethod + "|" + "控制层对应的实例：" + restController);
        //(这里很重要)响应设置（在这里设置的话更好点，不用在多出设置）
        //1.json响应设置
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");

        //2.跨域问题设置:
        // 设置允许跨域的域名，*表示允许所有域名访问
        response.setHeader("Access-Control-Allow-Origin", "*");
        // 设置允许跨域的方法
        response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        // 设置允许跨域的请求头
        response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
        // 是否允许携带凭证 (如 cookies)
        response.setHeader("Access-Control-Allow-Credentials", "true");


        if ("OPTIONS".equalsIgnoreCase(httpMethod)) {
            response.setStatus(HttpServletResponse.SC_OK);
            return;
        }


        //都不空才能处理请求
        if (handlerMethod != null && restController != null) {
            try {
                handlerMethod.setAccessible(true);//破坏访问属性
                Object[] args = resolveMethodParameters(handlerMethod, request);//处理请求获取控制层请求对应方法的参数
                System.out.println("转换后的参数：" + JSON.toJSONString(args));
                Object result = handlerMethod.invoke(restController, args);//执行方法

                // JSON 响应处理
                String jsonResponse = JSONObject.toJSONString(result);
                response.getWriter().write(jsonResponse);
            } catch (Exception e) {
                System.out.println("成功捕获异常，具体信息：");
                e.printStackTrace();
//                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error processing request");
                handleException(response, e);
            }
        } else {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Route not found");
        }
    }


    /**
     * 辅助方法：解析方请求的各种参数(主要为的是解析请求体参数和请求体内容)
     * 场景：处理请求获取控制层请求对应方法的参数
     * 规则：
     * 1. **`@RequestBody`**：用于接受 JSON 请求体，将其转换为方法参数对象。
     * 2. **`@RequestParam`**：用于从请求参数（URL 查询参数或表单参数）中获取特定的值并注入到方法参数中。
     * 3. **默认参数映射**：若没有指定注解，则按参数名匹配请求参数，类似 `@RequestParam` 的功能。
     *
     * @param method：请求对应的处理方法
     * @param request：请求
     * @return
     * @throws IOException
     */
    private static Object[] resolveMethodParameters(Method method, HttpServletRequest request) throws IOException {
        Parameter[] parameters = method.getParameters();
        Object[] args = new Object[parameters.length];

        String requestBody = null;
        if ("POST".equalsIgnoreCase(request.getMethod()) || "PUT".equalsIgnoreCase(request.getMethod())) {
            requestBody = request.getReader().lines().reduce("", String::concat);
        }

        for (int i = 0; i < parameters.length; i++) {
            Parameter parameter = parameters[i];
            if (parameter.isAnnotationPresent(RequestBody.class)) {
                //TODO 探究：类型的转换这么整？
                //DONE 数组类型是否可行，数组也能直接转换，太牛了
                args[i] = JSON.parseObject(requestBody, parameter.getType());
//                args[i]=convertToType(requestBody,parameter.getType());   //这个有问题
                //TODO 本处处理泛型还需要细究去
                // 请求体：{数组名：[]},像这样还要不要解析为链表（感觉解析为数组会不会不合适）
                // 对应接口：/api/mall/settleAccounts
                // 根据参数名映射或者注解中的value
//                //处理泛型集合类型
//                if(List.class.equals(parameter.getType())){
//                    // 获取泛型参数类型 T
//                    Type genericType = ((ParameterizedType) parameter.getParameterizedType()).getActualTypeArguments()[0];
//                    // 首先将整个 JSON 请求体解析为 JSONObject
//                    JSONObject jsonObject = JSON.parseObject(requestBody);//注意请求体中一般被{}包住了，是一个对象
//                    // 使用 JSON 工具解析为 List<T>
////                    args[i] = JSON.parseArray(requestBody, (Class<?>) genericType);
//                }else{
//                    //非泛型解析
//                    args[i] = JSON.parseObject(requestBody, parameter.getType());
//
//                }
                //到这里结束
            } else if (parameter.isAnnotationPresent(RequestParam.class)) {
                //TODO 待解决：处理数组
                //DONE 待优化：当注解的value为空时，默认以参数名为主
                //  解决失败：发现参数名为arg0

                // 获取 @RequestParam 注解的 value，若为空，则使用参数名
                String paramName = parameter.getAnnotation(RequestParam.class).value();
                // 如果 value 为空，则默认使用参数名作为参数名
                if (paramName.isEmpty()) {
                    paramName = parameter.getName();  // 获取参数名作为默认参数名
                }
                String paramValue = request.getParameter(paramName);
                args[i] = convertToType(paramValue, parameter.getType());
            } else {
                String paramValue = request.getParameter(parameter.getName());
                args[i] = convertToType(paramValue, parameter.getType());
            }
        }
        return args;
    }

    /**
     * 辅助方法：利用全局异常处理器进行异常处理
     *
     * @param response:响应
     * @param exception：发生的异常
     */
    private static void handleException(HttpServletResponse response, Exception exception) throws IOException {
        Throwable cause = exception.getCause() != null ? exception.getCause() : exception;

        // 找到能够处理的异常类型
        Class<? extends Throwable> handledExceptionType = findCouldHandleException(cause);

        // 如果找到对应的处理方法，则执行全局异常处理方法
        if (handledExceptionType != null) {
            Method handlerMethod = exceptionHandleMethod.get(handledExceptionType);
            Object handlerInstance = restControllerAdvices.get(handledExceptionType);

            try {
                // 调用异常处理方法
                Object result = handlerMethod.invoke(handlerInstance, cause);

                // 将结果转换为 JSON 格式并写入响应
                String jsonResponse = JSONObject.toJSONString(result);
                response.getWriter().write(jsonResponse);
//                response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                response.setStatus(HttpServletResponse.SC_OK);//这里是这么定义，这里面用result的code来定义
            } catch (Exception e) {
                e.printStackTrace();
                // 如果异常处理失败，返回 500 错误
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Error handling exception");
            }
        } else {
            // 如果没有找到处理器，返回 500 错误
            try {
                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unhandled exception");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 辅助方法：循环查找父类，在全局异常处理器中找到能够处理的异常
     *
     * @param ex 需要查找的异常
     * @return 能够处理的异常类，如果未找到则返回null
     */
    private static Class<? extends Throwable> findCouldHandleException(Throwable ex) {
        Class<?> exceptionClass = ex.getClass();
        while (exceptionClass != null) {
            if (exceptionHandleMethod.containsKey(exceptionClass)) {
                return exceptionClass.asSubclass(Throwable.class); // 返回能处理的异常类
                //确保返回的类型与方法声明的返回类型 Class<? extends Throwable> 一致。
            }
            exceptionClass = exceptionClass.getSuperclass(); // 向上查找父类
            //将每次循环时的 exceptionClass 更新为其父类，逐层向上查找。
        }
        return null;
    }


    //TODO 待优化：思考下，如果为长整型怎么办
    //  待看

    /**
     * 辅助方法：类型转换
     *
     * @param value
     * @param type
     * @return
     */
    private static Object convertToType(String value, Class<?> type) {
        //之前有问题，这里如果是long或者是自定义类型咋办？
//        if (value == null) return null;
//        if (type == Integer.class || type == int.class) {
//            return Integer.parseInt(value);
//        } else if (type == Double.class || type == double.class) {
//            return Double.parseDouble(value);
//        }
//        return value;
        if (value == null) return null;

        try {
            // 使用 ConvertUtils 转换基本类型和常见类型
                //TODO 后续有时间添加时间类型
                //  这个部分赶时间由GPT生成了
            // 处理 LocalDateTime 类型
            if (type == LocalDateTime.class) {
                // 处理常见的日期格式，包括带时间和不带时间的日期
                DateTimeFormatter formatter;

                // 如果是带时间的格式 (例如 yyyy-MM-dd HH:mm:ss)
                if (value.contains("T")) {
                    formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
                }
                // 如果是日期格式 (例如 yyyy-MM-dd)，就加上默认时间
                else if (value.matches("\\d{4}-\\d{2}-\\d{2}")) {
                    value += "T00:00:00"; // 加上默认时间
                    formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
                } else {
                    throw new DateTimeParseException("Invalid date time format", value, 0);
                }

                return LocalDateTime.parse(value, formatter);
            }

            // 处理 java.util.Date 类型
            if (type == Date.class) {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return sdf.parse(value);
            }
            return ConvertUtils.convert(value, type);
        } catch (Exception e) {
            System.out.println("Type conversion error for value: " + value + ", target type: " + type.getName());
            e.printStackTrace();
        }

        return value;
    }


}